// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-01-01 // Contains ... using System; using System.Diagnostics.Contracts; using System.Text; using System.Xml.Linq; using System.Xml.Serialization; using LargoCommon.Abstract; using LargoCommon.Interfaces; using LargoCommon.Midi; namespace LargoCommon.Music { /// Musical tone. /// Tone is defined in given bit Range with a given Loudness. [Serializable] [XmlRoot] public class MusicalStrike : GeneralOwner, IMusicalTone { #region Fields and variables /// Mark for musical pause. public const string CPause = " "; /// Mark for rhythmical bang. public const string CBeat = "!"; /// Mark for duration. public const string CRepeat = "-"; #endregion #region Constructors /// Initializes a new instance of the MusicalStrike class. Serializable. public MusicalStrike() { this.OrdinalIndex = -1; } /// Initializes a new instance of the MusicalStrike class. /// Type of tone. /// Rhythmical range of bits. /// Musical loudness. /// Number of musical bar. public MusicalStrike( MusicalToneType toneType, BitRange givenBitRange, MusicalLoudness loudness, int barNumber) { this.ToneType = toneType; this.BitRange = givenBitRange; this.Loudness = loudness; this.BarNumber = barNumber; } /// Initializes a new instance of the MusicalStrike class. /// Type of tone. /// Rhythmical order. /// Musical duration. /// Musical loudness. /// Number of musical bar. public MusicalStrike( MusicalToneType toneType, byte givenRhythmicOrder, byte givenDuration, MusicalLoudness loudness, int barNumber) { this.ToneType = toneType; this.RhythmicOrder = givenRhythmicOrder; this.BitFrom = 0; this.Duration = givenDuration; this.Loudness = loudness; this.BarNumber = barNumber; } /// /// Initializes a new instance of the class. /// /// The xml element. /// The rhythmic order. public MusicalStrike(XElement xelement, byte rorder) { Contract.Requires(xelement != null); //// if (xelement == null) { return; } //// this.bitRange = new BitRange(); //// this.bitRange.SetXElement(element); this.BarNumber = XmlSupport.ReadIntegerAttribute(xelement.Attribute("Bar")); this.BitFrom = XmlSupport.ReadByteAttribute(xelement.Attribute("Start")); this.Duration = XmlSupport.ReadByteAttribute(xelement.Attribute("Length")); this.BitRange = new BitRange(rorder, this.BitFrom, (byte)this.Duration); //// string s = LibSupport.ReadStringAttribute(xelement.Attribute("ToneType")); //// this.ToneType = string.IsNullOrEmpty(s) ? MusicalToneType.Empty : (MusicalToneType)Enum.Parse(typeof(MusicalToneType), s); this.ToneType = MusicalToneType.Rhythmic; this.Loudness = DataEnums.ReadAttributeMusicalLoudness(xelement.Attribute("Loudness")); var attrInstr = xelement.Attribute("Instrument"); if (attrInstr != null) { this.InstrumentNumber = XmlSupport.ReadByteAttribute(attrInstr); } else { this.InstrumentNumber = (byte)MidiMelodicInstrument.None; } } #endregion #region Properties - Xml /// Gets Xml representation. /// Property description. public virtual XElement GetXElement { get { var xe = new XElement( "Tick", new XAttribute("Bar", this.BarNumber), new XAttribute("Start", this.BitFrom), new XAttribute("Length", this.Duration)); if (this.Loudness != MusicalLoudness.MeanLoudness) { xe.Add(new XAttribute("Loudness", this.Loudness.ToString())); } return xe; } } #endregion #region Tone Properties /// Gets or sets type of musical tone. /// Property description. public MusicalToneType ToneType { get; set; } /// Gets or sets ordinal index of tone in musical track. /// Property description. public int OrdinalIndex { get; set; } /// Gets or sets loudness. /// Property description. public MusicalLoudness Loudness { get; set; } /// Gets or sets a value indicating whether property of musical tone. /// Property description. public bool IsReady { get; set; } /// Gets or sets bar number. /// Property description. public int BarNumber { get; set; } /// Gets or sets staff number (MusicXml). /// Property description. public byte Staff { get; set; } /// Gets or sets voice number (MusicXml). /// Property description. public byte Voice { get; set; } /// /// Gets or sets a value indicating whether this instance is from previous bar. /// /// /// True if this instance is from previous bar; otherwise, false. /// public bool IsFromPreviousBar { get; set; } /// /// Gets or sets a value indicating whether [continue to next bar]. /// /// /// True if [continue to next bar]; otherwise, false. /// public bool IsGoingToNextBar { get; set; } /// Gets property of musical tone. /// Property description. public int BitPosition => ((this.BarNumber - 1) * this.RhythmicOrder) + this.BitFrom; /// Gets or sets staff number (MusicXml). /// Property description. public byte RhythmicOrder { get; set; } /// Gets or sets BitFrom. /// Property description. public byte BitFrom { get; set; } /// Gets or sets staff number (MusicXml). /// Property description. public int Duration { get; set; } /// /// Gets or sets the bit range. /// /// /// The bit range. /// public BitRange BitRange { get { Contract.Ensures(Contract.Result() != null); var bitRange = new BitRange(this.RhythmicOrder, this.BitFrom, (byte)this.Duration); return bitRange; } set { var bitRange = value; if (bitRange == null) { return; } this.RhythmicOrder = bitRange.Order; this.BitFrom = bitRange.BitFrom; this.Duration = bitRange.Length; } } /// Gets BitTo. /// Property description. public byte BitTo => this.BitRange.BitTo; /// Gets a value indicating whether IsPause. /// General musical property. /// Returns value. public bool IsPause => (this.ToneType == MusicalToneType.Empty) || (this.Loudness == 0); /// /// Gets a value indicating whether this instance is empty. /// /// /// true if this instance is empty; otherwise, false. /// public virtual bool IsEmpty => this.IsPause; /// Gets or sets index to harmonic structure. /// Musical instrument. /// /// Property description. public byte InstrumentNumber { get; set; } #endregion #region Note Properties /// /// Gets or sets the length of the note. /// /// /// The length of the note. /// public NoteLength NoteLength { get; set; } /// Gets The MIDI note to modify (0x0 to 0x7F). /// General musical property. public virtual byte NoteNumber => 0; /// Gets the MIDI note to modify (0x0 to 0x7F). /// General musical property. public virtual string Note => string.Empty; #endregion #region Other Properties /// /// Gets the melodic identifier. /// /// /// The melodic identifier. /// public virtual string MelodicIdentifier => $"Tick#{this.BitFrom}/{this.Duration}"; /// /// Gets the rhythmic identifier. /// /// /// The rhythmic identifier. /// public virtual string RhythmicIdentifier => $"{this.BitFrom}/{this.Duration}"; #endregion #region Public static methods /// /// Gets the new musical tone. /// /// The given header. /// The quotient. /// The midi tone. /// The bar number. /// The real bar number. /// if set to true [melodic track]. /// /// Returns value. /// public static IMusicalTone GetNewMusicalTone( MusicalHeader givenHeader, int quotient, IMidiTone midiTone, int barNumber, int realBarNumber, bool melodicTrack) { //// this.Strip.Context.Header //// this.Status.IsMelodic IMusicalTone musTone; var bitFrom = (int)Math.Round((double)midiTone.StartTime / quotient, 0); var bitTo = (int)Math.Round((double)(midiTone.StartTime + midiTone.Duration) / quotient, 0) - 1; var rhythmicOrder = givenHeader.System.RhythmicOrder; var bitLimit = rhythmicOrder * realBarNumber; //// Zero bit of next bar var bitZero = bitLimit - rhythmicOrder; if (bitTo < bitZero) { return null; } var isFromPreviousBar = barNumber > midiTone.BarNumberFrom; var continueToNextBar = barNumber < midiTone.BarNumberTo && bitTo >= bitLimit; var barBitFrom = !isFromPreviousBar ? bitFrom % rhythmicOrder : 0; var barBitTo = !continueToNextBar ? bitTo % rhythmicOrder : rhythmicOrder - 1; var duration = barBitTo - barBitFrom + 1; if (duration <= 0) { return null; } var bitRange = new BitRange(rhythmicOrder, (byte)barBitFrom, (byte)duration); if (midiTone.Loudness == MusicalLoudness.None) { return null; /* 2019/02 Reduction of redundant pauses, here was == MusicalLoudness.None ==> mtone = MusicalPause.CreatePause(rhythmicOrder, (byte)barBitFrom, (byte)duration, barNumber); return mtone; */ } if (melodicTrack) { var hs = givenHeader.System.HarmonicSystem; musTone = MusicalTone.CreateMelodicTone(hs, bitRange, midiTone.NoteNumber, barNumber, midiTone.Loudness); } else { musTone = new MusicalStrike(MusicalToneType.Rhythmic, bitRange, midiTone.Loudness, barNumber); } musTone.ToneType = melodicTrack ? MusicalToneType.Melodic : MusicalToneType.Rhythmic; /* 2019/02 if (!(mtone is MusicalStrike musTone)) { return null; } */ ////if (midiTone.Loudness != 0) { musTone.InstrumentNumber = melodicTrack ? midiTone.InstrumentNumber : midiTone.NoteNumber; //// musTone.Channel = this.Status.IsMelodic ? midiTone.Channel : MidiChannel.DrumChannel; ////} //// else { musTone.IsFromPreviousBar = false; musTone.IsGoingToNextBar = false; } musTone.IsFromPreviousBar = isFromPreviousBar; musTone.IsGoingToNextBar = continueToNextBar; //// musTone.BarNumberTo = midiTone.BarNumberTo; return musTone; } /// /// Corrects the incorrect binding. /// /// The first tone. /// The next tone. public static void CorrectBadBinding(MusicalStrike firstTone, MusicalStrike nextTone) { if (firstTone == null || nextTone == null) { return; } if (firstTone.IsGoingToNextBar && !nextTone.IsFromPreviousBar) { firstTone.IsGoingToNextBar = false; } if (nextTone is MusicalTone nextMelTone && firstTone is MusicalTone firstMelTone && firstTone.IsGoingToNextBar && firstMelTone.Pitch.SystemAltitude != nextMelTone.Pitch.SystemAltitude) { firstTone.IsGoingToNextBar = false; nextTone.IsFromPreviousBar = false; } if (!firstTone.IsGoingToNextBar || firstTone.BarNumber == nextTone.BarNumber - 1) { return; } firstTone.IsGoingToNextBar = false; nextTone.IsFromPreviousBar = false; } #endregion #region Public methods /// /// Accept content of given tone. /// /// Musical Strike. public void SetMusicalTone(MusicalStrike tone) { if (tone == null) { return; } //// this.SetBitRange(tone.BitRange()); this.BitRange = (BitRange)tone.BitRange.Clone(); this.Loudness = tone.Loudness; this.ToneType = tone.ToneType; this.BarNumber = tone.BarNumber; if (tone.Properties != null) { this.CopyProperties(tone.Properties); } } /// /// Makes a deep copy of the GeneralOwner object. /// /// /// Returns object. /// public override object Clone() { return this.CloneTone(); } /// Makes a deep copy of the MusicalStrike object. /// Returns object. public virtual object CloneTone() { var mt = new MusicalStrike(this.ToneType, this.BitRange, this.Loudness, this.BarNumber) //// (this.BarNumber) { OrdinalIndex = this.OrdinalIndex }; return mt; } #endregion #region Public Midi Support /// /// Write real tone to midi collection. /// /// Midi event collection. /// Bar division. public void WriteTo(MidiEventCollection midiEvents, int barDivision) { if (midiEvents == null) { return; } //// BitRange br = this.BitRange(this.BarNumber); if (this.RhythmicOrder <= 0) { return; } const int startTime = 0; var duration = MusicalProperties.MidiDuration(this.RhythmicOrder, this.Duration, barDivision); var stopTime = startTime + duration; byte note = 0; //// byte channel = (byte)this.Channel; var loudness = (byte)MusicalLoudness.None; switch (this.ToneType) { case MusicalToneType.Rhythmic: { //// channel = (byte)MidiChannel.DrumChannel; if (this.InstrumentNumber == 0) { this.InstrumentNumber = (byte)MidiRhythmicInstrument.HighBongo; } loudness = (byte)this.Loudness; //// (byte)MusicalLoudness.MeanLoudness; note = this.InstrumentNumber; break; } case MusicalToneType.Melodic: { if (!this.IsPause && (this.Loudness > 0)) { //// melTone != null && (melTone.Pitch != null) && if (this is MusicalTone melTone && melTone.IsTrueTone) { note = melTone.Pitch.MidiKeyNumber; //// (byte)melTone.Pitch.MidiPitchBend(), loudness = (byte)melTone.Loudness; //// channel = (byte)this.Channel; } } break; } } midiEvents.PutNote(startTime, note, stopTime, loudness, this.IsFromPreviousBar, this.IsGoingToNextBar); } /// /// Write Tone Events. /// /// Midi event collection. /// Bar division. /// Delta Time of bit. /// Delta Time of bar. /// Delta Time Shift. /// Returns value. public bool WriteTo(MidiEventCollection midiEvents, int barDivision, int bitDuration, int barDuration, int deltaTimeShift) { //// cyclomatic complexity 10:13 if (midiEvents == null) { return false; } if (this.RhythmicOrder <= 0) { return false; } var mtone = this; var bitRangeTo = mtone.BitRange; //// (mtone.BarNumberTo) if (mtone.RhythmicOrder <= 0 || bitRangeTo == null) { return false; } var barStartTime = 1 + (barDuration * (mtone.BarNumber - 1)) - deltaTimeShift; var startTime = barStartTime + (bitDuration * mtone.BitFrom); //// lastTotalTime = 0 var stopTime = barStartTime + (bitDuration * (mtone.BitTo + 1)) - 1; if (stopTime <= startTime) { return false; } //// midiEvent = this.GetNoteOff(deltaTime2 - deltaTimeShift - 1); //// Temporary - decreased by 1, see comment on SortByStartTime switch (this.ToneType) { case MusicalToneType.Rhythmic: { this.WriteRhythmicToneTo(midiEvents, startTime, stopTime); break; } case MusicalToneType.Melodic: { this.WriteMelodicToneTo(midiEvents, startTime, stopTime); break; } } return true; } #endregion #region String representation /// String representation of the object. /// Returns value. public virtual string ToShortString() { var s = new StringBuilder(); if (this.Loudness == 0) { s.Append(MusicalStrike.CPause); } return s.ToString(); } /// String representation of the object. /// Returns value. public override string ToString() { var s = new StringBuilder(string.Empty); //// if (this is MusicalTone) { return s.ToString(); } //// s.AppendFormat(CultureInfo.CurrentCulture, "{0,3}", this.Loudness > 0 ? MusicalStrike.CBeat : MusicalStrike.CPause); //// for (byte lev = 2; lev <= this.Duration; lev++) { //// s.AppendFormat(CultureInfo.CurrentCulture, "{0,3}", MusicalStrike.CRepeat); } s.AppendFormat(" <{0,3}>", this.Duration); //// s.Append(",lev" + Loudness.ToString(System.Globalization.CultureInfo.CurrentCulture.NumberFormat) + ")"); //// s.Append(base.ToString()); return s.ToString(); } /// /// Rhythmic to string. /// /// Returns value. public string RhythmicToString() { var s = new StringBuilder(string.Empty); //// if (this is MusicalTone) { return s.ToString(); } s.AppendFormat(" <{0,3}>", this.Duration); return s.ToString(); } #endregion #region Private methods /// /// Writes the melodic tone to. /// /// The midi events. /// The start time. /// The stop time. private void WriteMelodicToneTo(MidiEventCollection midiEvents, int startTime, int stopTime) { byte note = 0, loudness = (byte)MusicalLoudness.None; if (!this.IsPause && (this.Loudness > 0)) { //// melTone != null && (melTone.Pitch != null) && if (this is MusicalTone melTone && melTone.IsTrueTone) { note = melTone.Pitch.MidiKeyNumber; //// (byte)melTone.Pitch.MidiPitchBend(), loudness = (byte)melTone.Loudness; } } if (note > 0) { midiEvents.PutNote(startTime, note, stopTime, loudness, this.IsFromPreviousBar, this.IsGoingToNextBar); } } /// /// Writes the rhythmic tone to. /// /// The midi events. /// The start time. /// The stop time. private void WriteRhythmicToneTo(MidiEventCollection midiEvents, int startTime, int stopTime) { Contract.Requires(midiEvents != null); if (this.InstrumentNumber == 0) { this.InstrumentNumber = (byte)MidiRhythmicInstrument.HighBongo; } var loudness = (byte)this.Loudness; var note = this.InstrumentNumber; //// if (note > 0) { midiEvents.PutNote(startTime, note, stopTime, loudness, this.IsFromPreviousBar, this.IsGoingToNextBar); //// } } #endregion } }